﻿#region File Description
/****************************************************************************
*                                                                           *
* Game Manager.                                                             *
*                                                                           *
* Copyright (c) Ionescu Marius. All rights reserved(2013).                  *
*                                                                           *
****************************************************************************/
#endregion
namespace Silverlight3D
{
    using System;
    using System.Collections.Generic;
    using System.Windows.Media;
    using System.Windows.Shapes;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media.Imaging;

    /// <summary>
    /// A rectangular VectorPolygon where all four corners lie in the same plane.
    /// An image or other content can be rendered upon the rectangle rather than a plain color.
    /// </summary>
    public class VectorRect : VectorPolygon
    {
        public Vector Vector0
        {
            get { return this.Vectors[0]; }
            set { this.Vectors[0] = value; }
        }

        public Vector Vector1
        {
            get { return this.Vectors[1]; }
            set { this.Vectors[1] = value; }
        }

        /// <summary>
        /// VectorRect vertices must always lie in the same plane (and adding content to the rectangle will not work otherwise).
        /// So calculate the last vector based on the other three. (It's Vector2 rather than Vector3 so that the resulting vectors are
        /// kept in anti-clockwise order; a requirement for the shading and backfacing calculations.)
        /// </summary>
        public Vector Vector2
        {
            get { return this.Vector1 + this.Vector3 - this.Vector0; }
        }

        public Vector Vector3
        {
            get { return this.Vectors[2]; }
            set { this.Vectors[2] = value; }
        }

        public UIElement Content { get; set; }
        public Size ContentSize { get; set; }

        public VectorRect()
            : base()
        {
            this.Vectors = new List<Vector>();
            this.Vectors.Add(null);
            this.Vectors.Add(null);
            this.Vectors.Add(null);
        }

        public override double GetMaxZAfterRotation(Projector projector, Vector rotationCenter)
        {
            double maxZ = projector.Rotate(this.Vector0, rotationCenter).Z;

            double z1 = projector.Rotate(this.Vector1, rotationCenter).Z;
            if (z1 > maxZ)
            {
                maxZ = z1;
            }

            double z2 = projector.Rotate(this.Vector2, rotationCenter).Z;
            if (z2 > maxZ)
            {
                maxZ = z2;
            }

            double z3 = projector.Rotate(this.Vector3, rotationCenter).Z;
            if (z3 > maxZ)
            {
                maxZ = z3;
            }

            return maxZ;
        }

        public override Vector Center()
        {
            Vector result = new Vector();

            result = result + Vector0;
            result = result + Vector1;
            result = result + Vector2;
            result = result + Vector3;

            result = result * 0.25;

            return result;
        }

        public override UIElement Project(Projector projector, IShader shader, Vector rotationCenter)
        {
            return this.Project(projector, shader, null, rotationCenter);
        }

        public override UIElement Project(Projector projector, IShader shader, Vector viewPosition, Vector rotationCenter)
        {
            if (viewPosition == null)
            {
                viewPosition = projector.ViewPosition;
            }

            if (this.HideIfBackFacing && this.IsBackFacing(projector, rotationCenter))
            {
                return null;
            }

            IList<Point> points = new List<Point>();

            var point0 = projector.Project(this.Vector0, viewPosition, rotationCenter);

            if (point0 == null)
            {
                return null;
            }

            points.Add(point0.Value);

            var point1 = projector.Project(this.Vector1, viewPosition, rotationCenter);

            if (point1 == null)
            {
                return null;
            }

            points.Add(point1.Value);

            var point2 = projector.Project(this.Vector2, viewPosition, rotationCenter);

            if (point2 == null)
            {
                return null;
            }

            points.Add(point2.Value);

            var point3 = projector.Project(this.Vector3, viewPosition, rotationCenter);

            if (point3 == null)
            {
                return null;
            }

            points.Add(point3.Value);

            Matrix3DTransformer transformer = new Matrix3DTransformer();
            Matrix3DProjection projection = new Matrix3DProjection();

            projection.ProjectionMatrix = transformer.CalculateNewTransform(this.ContentSize, points[0], points[1], points[3], points[2]);

            UIElement element = Content;
            element.Projection = projection;

            return element;
        }

        public override VectorObject Clone()
        {
            var result = new VectorRect();

            result.Vector0 = this.Vector0;
            result.Vector1 = this.Vector1;
            result.Vector3 = this.Vector3;

            result.FillColor = this.FillColor;
            result.Content = this.Content;
            result.ContentSize = this.ContentSize;

            return result;
        }

        /// <summary>
        /// Returns true if the VectorRect is back facing and therefore doesn't need to be rendered.
        /// </summary>
        private bool IsBackFacing(Projector projector, Vector rotationCenter)
        {
            if (rotationCenter == null)
            {
                rotationCenter = new Vector(0, 0, 0);
            }

            var rotatedVectorRect = projector.Rotate(this, rotationCenter);

            var edge1 = rotatedVectorRect.Vector0 - rotatedVectorRect.Vector1;
            var edge2 = rotatedVectorRect.Vector0 - rotatedVectorRect.Vector3;

            var normal = VectorHelper.CrossProduct(edge1, edge2);

            var vectorRectToViewPosition = projector.ViewPosition.AddZ(-projector.ScreenDistance) - rotatedVectorRect.Center();

            var dotProduct = VectorHelper.DotProduct(normal, vectorRectToViewPosition);

            return dotProduct <= 0;
        }
    }
}